package main

import (
	"fmt"
	"sync"
)

/*
*
手写协程池

解题思路：
1.定义协程池结构体：首先，我们需要定义一个协程池的结构体，包含协程池的属性和方法。结构体中需要包含一个任务队列、协程池的大小、当前运行的协程数量等属性。
2. 初始化协程池：在初始化函数中，我们需要创建一个指定大小的任务队列，并初始化协程池的属性。
3. 添加任务到协程池：当有任务需要执行时，我们将任务添加到任务队列中。
4. 启动协程池：在启动函数中，我们需要根据协程池的大小创建对应数量的协程，并从任务队列中获取任务进行执行。每个协程会不断从任务队列中获取任务并执行，直到任务队列为空。
5. 控制协程数量：在协程池中，我们需要控制同时运行的协程数量，以防止过多的协程导致资源浪费。可以使用信号量或者计数器来控制协程的数量。

通过以上的解题思路，我们可以实现一个基本的协程池。在实际应用中，可能还需要考虑一些其他的因素，如任务优先级、任务超时处理等。根据具体的需求，可以对协程池进行进一步的扩展和优化。
*/

// Job 任务结构体
type Job struct {
	ID int
}

// Worker 协程结构体
type Worker struct {
	ID         int
	JobChannel chan Job
	Quit       chan bool
}

// Pool 协程池结构体
type Pool struct {
	WorkerNum   int
	JobChannel  chan Job
	WorkerQueue chan chan Job
	Quit        chan bool
	wg          sync.WaitGroup
}

// NewWorker 创建一个新的工作者
func NewWorker(id int, workerQueue chan chan Job) Worker {
	return Worker{
		ID:         id,
		JobChannel: make(chan Job),
		Quit:       make(chan bool),
	}
}

// Start 启动工作者
func (w Worker) Start(workerQueue chan chan Job) {
	go func() {
		for {
			workerQueue <- w.JobChannel //获取worker任务队列, 加入协程池的管道
			select {
			case job := <-w.JobChannel: //从当前worker的JobChannel中获取job, 并执行任务
				fmt.Printf("Worker %d started job %d\n", w.ID, job.ID)
				// 执行任务
				fmt.Printf("Worker %d finished job %d\n", w.ID, job.ID)
			case <-w.Quit: //监听退出指令,如果当前Worker收到了暂停指令, 那么会进行退出
				return
			}
		}
	}()
}

// Start 启动工作者池
func (p *Pool) Start() {
	for i := 0; i < p.WorkerNum; i++ { //创建池大小个工作worker
		worker := NewWorker(i, p.WorkerQueue)
		worker.Start(p.WorkerQueue) //每个worker调用自己的start()方法
	}

	//启动一个协程, 监听: 任务管道 + 退出信号管道
	go func() {
		for {
			select {
			case job := <-p.JobChannel:
				worker := <-p.WorkerQueue //获取一个worker的任务管道
				worker <- job             //向获取到的任务管道中, 添加任务
			case <-p.Quit: //监听关闭协程池信号
				for i := 0; i < p.WorkerNum; i++ { //获取所有的任务管道
					worker := <-p.WorkerQueue
					worker <- Job{} // 发送空任务，通知协程退出
				}
				p.wg.Done()
				return
			}
		}
	}()
}

// AddJob 添加作业到作业通道
func (p *Pool) AddJob(job Job) {
	p.JobChannel <- job //直接加入任务管道
}

// Stop 停止工作者池
func (p *Pool) Stop() {
	p.Quit <- true //向任务退出管道中添加一个退出标记
	p.wg.Wait()    //调用waitGroup等待
}

func main() {
	pool := Pool{
		WorkerNum:   5,                      //协程数量
		JobChannel:  make(chan Job),         //任务管道
		WorkerQueue: make(chan chan Job, 5), //worker队列, 存放5个任务管道
		Quit:        make(chan bool),        //退出标记管道
	}

	pool.Start()

	//创建十个任务, 交给协程池执行
	for i := 0; i < 10; i++ {
		job := Job{ID: i}
		pool.AddJob(job)
	}

	pool.Stop()
}
